// 缓存装饰器

import 'reflect-metadata';
import { CacheManager } from '../managers/CacheManager';
import { CacheOptions } from '../types/cache';

// 全局缓存管理器实例
let globalCacheManager: CacheManager | null = null;

/**
 * 设置全局缓存管理器
 */
export function setCacheManager(cacheManager: CacheManager): void {
  globalCacheManager = cacheManager;
}

/**
 * 获取全局缓存管理器
 */
function getCacheManager(): CacheManager {
  if (!globalCacheManager) {
    throw new Error('Cache manager not initialized. Call setCacheManager() first.');
  }
  return globalCacheManager;
}

export interface CacheDecoratorOptions extends CacheOptions {
  key?: string | ((args: any[]) => string);
  condition?: (args: any[]) => boolean;
  unless?: (args: any[]) => boolean;
}

/**
 * 缓存装饰器
 */
export function Cache(options: CacheDecoratorOptions = {}): any {
  return function (
    target: any,
    propertyKey: string | symbol,
    descriptor?: PropertyDescriptor
  ): PropertyDescriptor | void {
    if (!descriptor) {
      throw new Error('@Cache can only be applied to methods');
    }

    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@Cache can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const cacheManager = getCacheManager();

      // 检查条件
      if (options.condition && !options.condition(args)) {
        return originalMethod.apply(this, args);
      }

      if (options.unless?.(args)) {
        return originalMethod.apply(this, args);
      }

      // 生成缓存键
      const cacheKey = generateCacheKey(target, propertyKey, args, options.key);

      try {
        // 尝试从缓存获取
        const cached = await cacheManager.get(cacheKey);
        if (cached !== null) {
          return cached;
        }

        // 执行原方法
        const result = await originalMethod.apply(this, args);

        // 缓存结果
        const cacheOptions: CacheOptions = {};
        if (options.ttl !== undefined) cacheOptions.ttl = options.ttl;
        if (options.tags !== undefined) cacheOptions.tags = options.tags;
        if (options.compress !== undefined) cacheOptions.compress = options.compress;
        if (options.serialize !== undefined) cacheOptions.serialize = options.serialize;

        await cacheManager.set(cacheKey, result, cacheOptions);

        return result;
      } catch (error) {
        // 缓存失败时仍然执行原方法
        console.warn(`Cache operation failed for ${cacheKey}:`, error);
        return originalMethod.apply(this, args);
      }
    };

    return descriptor;
  };
}

/**
 * 缓存失效装饰器
 */
export function CacheEvict(
  options: { key?: string | ((args: any[]) => string); allEntries?: boolean } = {}
): MethodDecorator {
  return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@CacheEvict can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const result = await originalMethod.apply(this, args);

      try {
        const cacheManager = getCacheManager();

        if (options.allEntries) {
          // 清空所有缓存
          await cacheManager.clear();
        } else {
          // 删除特定缓存
          const cacheKey = generateCacheKey(target, propertyKey, args, options.key);
          await cacheManager.del(cacheKey);
        }
      } catch (error) {
        console.warn('Cache eviction failed:', error);
      }

      return result;
    };

    return descriptor;
  };
}

/**
 * 缓存更新装饰器
 */
export function CachePut(options: CacheDecoratorOptions = {}): MethodDecorator {
  return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@CachePut can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const result = await originalMethod.apply(this, args);

      try {
        const cacheManager = getCacheManager();
        const cacheKey = generateCacheKey(target, propertyKey, args, options.key);

        const cacheOptions: CacheOptions = {};
        if (options.ttl !== undefined) cacheOptions.ttl = options.ttl;
        if (options.tags !== undefined) cacheOptions.tags = options.tags;
        if (options.compress !== undefined) cacheOptions.compress = options.compress;
        if (options.serialize !== undefined) cacheOptions.serialize = options.serialize;

        await cacheManager.set(cacheKey, result, cacheOptions);
      } catch (error) {
        console.warn('Cache put failed:', error);
      }

      return result;
    };

    return descriptor;
  };
}

/**
 * 生成缓存键
 */
function generateCacheKey(
  target: any,
  propertyKey: string | symbol,
  args: any[],
  keyOption?: string | ((args: any[]) => string)
): string {
  if (typeof keyOption === 'function') {
    return keyOption(args);
  }

  if (typeof keyOption === 'string') {
    // 支持模板字符串，如 "user:${args[0]}"
    return keyOption.replace(/\$\{args\[(\d+)\]\}/g, (match, index) => {
      const argIndex = parseInt(index, 10);
      return args[argIndex] !== undefined ? String(args[argIndex]) : match;
    });
  }

  // 默认键生成策略
  const className = target.constructor.name;
  const methodName = typeof propertyKey === 'symbol' ? propertyKey.toString() : propertyKey;
  const argsHash = generateArgsHash(args);

  return `${className}:${methodName}:${argsHash}`;
}

/**
 * 生成参数哈希
 */
function generateArgsHash(args: any[]): string {
  if (args.length === 0) {
    return 'no-args';
  }

  try {
    const argsString = JSON.stringify(args);
    // 简单哈希函数
    let hash = 0;
    for (let i = 0; i < argsString.length; i++) {
      const char = argsString.charCodeAt(i);
      hash = (hash << 5) - hash + char;
      hash = hash & hash; // 转换为32位整数
    }
    return Math.abs(hash).toString(36);
  } catch {
    // 如果参数无法序列化，使用参数长度和类型
    return args.map((arg, index) => `${index}:${typeof arg}`).join('-');
  }
}

/**
 * 缓存配置装饰器
 */
export function CacheConfig(config: CacheDecoratorOptions) {
  return function (_target: any) {
    Reflect.defineMetadata('cache:config', config, _target);
  };
}

/**
 * 获取类的缓存配置
 */
export function getCacheConfig(target: any): CacheDecoratorOptions | undefined {
  return Reflect.getMetadata('cache:config', target);
}

/**
 * 条件缓存装饰器
 */
export function CacheIf(condition: (args: any[]) => boolean, options: CacheDecoratorOptions = {}) {
  return Cache({
    ...options,
    condition,
  });
}

/**
 * 排除条件缓存装饰器
 */
export function CacheUnless(
  condition: (args: any[]) => boolean,
  options: CacheDecoratorOptions = {}
) {
  return Cache({
    ...options,
    unless: condition,
  });
}

/**
 * 带标签的缓存装饰器
 */
export function CacheWithTags(tags: string[], options: Omit<CacheDecoratorOptions, 'tags'> = {}) {
  return Cache({
    ...options,
    tags,
  });
}

/**
 * 按标签失效缓存装饰器
 */
export function CacheEvictByTag(tag: string): MethodDecorator {
  return function (_target: any, _propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@CacheEvictByTag can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const result = await originalMethod.apply(this, args);

      try {
        const cacheManager = getCacheManager();
        await cacheManager.invalidateByTag(tag);
      } catch (error) {
        console.warn(`Cache eviction by tag ${tag} failed:`, error);
      }

      return result;
    };

    return descriptor;
  };
}

// 导出常用的缓存键生成函数
export const CacheKeyGenerators = {
  /**
   * 基于第一个参数生成键
   */
  firstArg:
    (prefix: string = '') =>
    (args: any[]) =>
      `${prefix}${prefix ? ':' : ''}${args[0]}`,

  /**
   * 基于多个参数生成键
   */
  multiArgs:
    (prefix: string = '', separator: string = ':') =>
    (args: any[]) =>
      `${prefix}${prefix ? separator : ''}${args.join(separator)}`,

  /**
   * 基于对象属性生成键
   */
  objectProperty:
    (prefix: string = '', property: string) =>
    (args: any[]) => {
      const obj = args[0];
      return `${prefix}${prefix ? ':' : ''}${obj?.[property] ? obj[property] : 'unknown'}`;
    },

  /**
   * 基于用户ID生成键
   */
  userId:
    (prefix: string = 'user') =>
    (args: any[]) =>
      `${prefix}:${args[0]}`,

  /**
   * 基于时间窗口生成键
   */
  timeWindow:
    (prefix: string = '', windowSize: number = 3600) =>
    (args: any[]) => {
      const now = Math.floor(Date.now() / 1000);
      const window = Math.floor(now / windowSize) * windowSize;
      return `${prefix}${prefix ? ':' : ''}${window}:${args.join(':')}`;
    },
};
